package com.ocom.oauth.config;

import com.ocom.oauth.auth.aiotweb.AiotUserDetailService;
import com.ocom.oauth.auth.aiotweb.AiotWebTokenGranter;
import com.ocom.oauth.auth.ali.AliTokenGranter;
import com.ocom.oauth.auth.ali.AliUserDetailService;
import com.ocom.oauth.auth.app.AppCampusTokenGranter;
import com.ocom.oauth.auth.app.AppCampusUserDetailService;
import com.ocom.oauth.auth.app.AppPwdTokenGranter;
import com.ocom.oauth.auth.app.AppPwdUserDetailService;
import com.ocom.oauth.auth.replace.ReplaceTokenDetailService;
import com.ocom.oauth.auth.replace.ReplaceTokenGranter;
import com.ocom.oauth.auth.sms.SmsTokenGranter;
import com.ocom.oauth.auth.sms.SmsUserDetailService;
import com.ocom.oauth.auth.app.AppCodeTokenGranter;
import com.ocom.oauth.auth.app.AppCodeUserDetailService;
import com.ocom.oauth.auth.web.CodeTokenGranter;
import com.ocom.oauth.auth.web.CustomUserDetailService;
import com.ocom.oauth.auth.wx.WxTokenGranter;
import com.ocom.oauth.auth.wx.WxUserDetailService;
import com.ocom.oauth.auth.xcx.XcxTokenGranter;
import com.ocom.oauth.auth.xcx.XcxUserDetailService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.CompositeTokenGranter;
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.*;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * oauth认证服务器配置
 *
 */
@Configuration
@RequiredArgsConstructor
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

	/** 令牌持久化配置 */
	private final TokenStore tokenStore;
	/** 客户端详情服务 */
	private final ClientDetailsService clientDetailsService;
	/** 用户详情服务 */
	private final UserDetailsService userDetailsService;
	/** 认证管理器 */
	private final AuthenticationManager authenticationManager;
	/** 授权码服务 */
	private final AuthorizationCodeServices authorizationCodeServices;
	/** jwtToken解析器 */
	@Autowired
	private JwtAccessTokenConverter jwtAccessTokenConverter;
	/** token增强器 */
	@Autowired
	private TokenEnhancer tokenEnhancer;

	//自定义验证码验证
	@Autowired
	private CustomUserDetailService customUserDetailService;

	//自定义微信验证
	@Autowired
	private WxUserDetailService wxUserDetailService;

	//自定义微信用户小程序验证
	@Autowired
	private XcxUserDetailService xcxUserDetailService;

	//自定义Aiotweb验证
	@Autowired
	private AiotUserDetailService aiotUserDetailService;

	//自定义手机验证
	@Autowired
	private SmsUserDetailService smsUserDetailService;

	@Autowired
	private AppCodeUserDetailService appCodeUserDetailService;

	@Autowired
	private AppPwdUserDetailService appPwdUserDetailService;

	@Autowired
	private ReplaceTokenDetailService replaceTokenDetailService;

	@Autowired
	private AliUserDetailService aliUserDetailService;

	@Autowired
	private AppCampusUserDetailService appCampusUserDetailService;

	/**
	 * 客户端详情服务配置 （demo采用本地内存存储）
	 */
	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
		clients
				// 使用本地内存存储
				.inMemory()
				// web客户端id
				.withClient("client_1")
				// 客户端密码
				.secret(new BCryptPasswordEncoder().encode("123456"))
				// 该客户端允许授权的类型
//				.authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token")
				.authorizedGrantTypes( "code_auth", "refresh_token")
				// 该客户端允许授权的范围
				.scopes("all")
				// false跳转到授权页面，true不跳转，直接发令牌
				.autoApprove(false)
				.and()
				// 小程序客户端id
				.withClient("client_2")
				// 客户端密码
				.secret(new BCryptPasswordEncoder().encode("123456"))
				// 该客户端允许授权的类型
				.authorizedGrantTypes( "wx_auth", "refresh_token")
				// 该客户端允许授权的范围
				.scopes("all")
				.and()
				// 用户小程序客户端id
				.withClient("client_3")
				//设置过期时间
//				.accessTokenValiditySeconds(100)
				// 客户端密码
				.secret(new BCryptPasswordEncoder().encode("123456"))
				// 该客户端允许授权的类型
				.authorizedGrantTypes( "wx_login", "refresh_token")
				// 该客户端允许授权的范围
				.scopes("all")
				.and()
				// 用户小程序客户端id
				.withClient("client_4")
				//设置过期时间
//				.accessTokenValiditySeconds(100)
				// 客户端密码
				.secret(new BCryptPasswordEncoder().encode("123456"))
				// 该客户端允许授权的类型
				.authorizedGrantTypes( "aiot_auth", "refresh_token")
				// 该客户端允许授权的范围
				.scopes("all")
				.and()
				// 手机验证码客户端id
				.withClient("client_5")
				//设置过期时间
//				.accessTokenValiditySeconds(100)
				// 客户端密码
				.secret(new BCryptPasswordEncoder().encode("123456"))
				// 该客户端允许授权的类型
				.authorizedGrantTypes( "sms_auth", "refresh_token")
				// 该客户端允许授权的范围
				.scopes("all")
				.and()
				// 手机验证码客户端id
				.withClient("client_6")
				//设置过期时间
//				.accessTokenValiditySeconds(100)
				// 客户端密码
				.secret(new BCryptPasswordEncoder().encode("123456"))
				// 该客户端允许授权的类型
				.authorizedGrantTypes( "replace_token", "refresh_token")
				// 该客户端允许授权的范围
				.scopes("all")
				.and()
				// 用户阿里小程序客户端id
				.withClient("client_7")
				.secret(new BCryptPasswordEncoder().encode("123456"))
				// 该客户端允许授权的类型
				.authorizedGrantTypes( "alixcx_login", "refresh_token")
				// 该客户端允许授权的范围
				.scopes("all")
				.and()
				.withClient("client_8")
				.secret(new BCryptPasswordEncoder().encode("123456"))
				.authorizedGrantTypes( "app_auth_code", "refresh_token")
				.scopes("all")
				.and()
				.withClient("client_9")
				.secret(new BCryptPasswordEncoder().encode("123456"))
				.authorizedGrantTypes( "app_auth_pwd", "refresh_token")
				.scopes("all")
				.and()
				.withClient("client_10")
				.secret(new BCryptPasswordEncoder().encode("123456"))
				.authorizedGrantTypes( "app_campus", "refresh_token")
				.scopes("all")
				// false跳转到授权页面，true不跳转，直接发令牌
				.autoApprove(false);



	}

	/**
	 * 配置访问令牌端点
	 */
	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) {

		//在原有的基础上增加自定义验证码登录
		List<TokenGranter> tokenGranters = getTokenGranters(endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory());
		tokenGranters.add(endpoints.getTokenGranter());

		endpoints
				.tokenGranter(new CompositeTokenGranter(tokenGranters))
				// 令牌管理
				.tokenStore(tokenStore)
				// 认证管理器
				.authenticationManager(authenticationManager)
				// 授权码服务
				.authorizationCodeServices(authorizationCodeServices)
				// 用户详情服务 refresh_token需要userDetailsService
				.reuseRefreshTokens(false)
				.userDetailsService(userDetailsService)
				// 允许token端点的请求方式
				.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
				.pathMapping("/oauth/token","/login");  //覆盖登录地址

		endpoints.tokenServices(tokenServices());

		// 令牌管理服务 yoci.security.oauth2.tokenStore.type=rsa-jwt-server 那么使用的是JWT
		if (jwtAccessTokenConverter != null && tokenEnhancer != null) {
			TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
			tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer, jwtAccessTokenConverter));
			endpoints.tokenEnhancer(tokenEnhancerChain);
		}
	}


	/**
	 * 自定义TokenGranter集合
	 */
	private List<TokenGranter> getTokenGranters(AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) {
		ArrayList<TokenGranter>  tokenGranterArrayList=new ArrayList<>();
		tokenGranterArrayList.add(new CodeTokenGranter(tokenServices, clientDetailsService, requestFactory, customUserDetailService));
		tokenGranterArrayList.add(new WxTokenGranter(tokenServices, clientDetailsService, requestFactory, wxUserDetailService));
		tokenGranterArrayList.add(new XcxTokenGranter(tokenServices, clientDetailsService, requestFactory, xcxUserDetailService));
		tokenGranterArrayList.add(new AiotWebTokenGranter(tokenServices, clientDetailsService, requestFactory, aiotUserDetailService));
		tokenGranterArrayList.add(new SmsTokenGranter(tokenServices, clientDetailsService, requestFactory, smsUserDetailService));
		tokenGranterArrayList.add(new AppCodeTokenGranter(tokenServices, clientDetailsService, requestFactory, appCodeUserDetailService));
		tokenGranterArrayList.add(new AppPwdTokenGranter(tokenServices, clientDetailsService, requestFactory, appPwdUserDetailService));
		tokenGranterArrayList.add(new ReplaceTokenGranter(tokenServices, clientDetailsService, requestFactory, replaceTokenDetailService));
		tokenGranterArrayList.add(new AliTokenGranter(tokenServices, clientDetailsService, requestFactory, aliUserDetailService));
		tokenGranterArrayList.add(new AppCampusTokenGranter(tokenServices, clientDetailsService, requestFactory, appCampusUserDetailService));
		return tokenGranterArrayList;
	}


	/**
	 * 配置令牌端点安全约束
	 */
	@Override
	public void configure(AuthorizationServerSecurityConfigurer security) {
		security
				// oauth/check_token公开
				.checkTokenAccess("permitAll()")
				// oauth/token_key 公开密钥
				.tokenKeyAccess("permitAll()")
				// 允许表单认证
				.allowFormAuthenticationForClients();
	}

	/**
	 * 令牌服务配置
	 * 
	 * @return 令牌服务对象
	 */
	public AuthorizationServerTokenServices tokenServices() {
		DefaultTokenServices tokenServices = new DefaultTokenServices();
		tokenServices.setTokenStore(tokenStore);
		tokenServices.setSupportRefreshToken(true);
		// 令牌增强
		TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
		tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer, jwtAccessTokenConverter));
		tokenServices.setTokenEnhancer(tokenEnhancerChain);
		// 令牌默认有效期20小时
		tokenServices.setAccessTokenValiditySeconds(72000);
		// 刷新令牌默认有效期3天
		tokenServices.setRefreshTokenValiditySeconds(259200);
		return tokenServices;
	}


	public static void main(String[] args){
		System.out.println(new BCryptPasswordEncoder().encode("123456m92g4sPI"));
	}
}
